home *** CD-ROM | disk | FTP | other *** search
- Abstract:
- This document describes some of the planned implementation details
- of the mmu.library and the memory.library. Everything is highly
- preliminary and open for discussion.
-
- mmu.library:
- The library startup function must check the following:
-
- - which processor is installed and which type of MMU is used. MMU types
- are usually implicit to the CPU type except for the 68851 MMU which has
- to be checked explicitly.
-
- - Scan an already existing MMU table and build an abstraction of that MMU
- table called the global context.
- This routine is of course processor specific.
- OR, if no MMU table has been build, provide one by scanning the memory
- in the system, using the expansion library for scanning for expansion
- cards. Special hardware that is only present in the later amiga models
- must be checked as well. The detailed procedure can be found in the
- enforcer code.
-
- - Rebuild the library base and the function vectors aligned in some page
- of memory and delete the version that was created by MakeLibrary().
- This step requires some meta-magic but must be done to allow the library
- to protect itself from unauthorized access, as for example for a future
- multi user processing.
-
- - Build the MMU tables from the global context and install it.
-
- The MMU library should really build TWO MMU tables, a user table for
- code running in user mode. This is represented by the MMU table of the
- context. And a supervisor MMU table which is identically to the MMU
- table that was found before the library was build.
-
-
- Context switches:
-
- To be able to provide individual contexts for each task, the mmu table must
- be exchanged on task switch. That can be done either by patching some exec
- functions or by setting the tc_Switch/tc_Launch pointers of the task. I
- would recommend the second alternative because patches have usually a problem
- of their own. Sigh.
-
- To be able to access library code safely on a context switch, a user MMU table
- and a supervisor MMU table must be build. As the tc_Switch/tc_Launch functions
- are executed in supervisor code, they can freely access the library base.
-
- Since no data pointer of any kind is available in the task structure (except
- a UserData pointer, but that should be available for the user and shouldn't
- be used up by the mmu.library), the identification of the "Context" of a
- task requires special care. The context structure might look, for example,
- like this:
-
- struct Context {
- ULONG ctx_MagicCookie;
- struct Context *ctx_MySelf; /* for identifcation a pointer to itself */
- .....
- UWORD ctx_SwitchInst; /* a 4eb9 - jsr Abs.l */
- void (*ctx_SwitchEnter)(); /* address of the library function */
- UWORD ctx_LaunchInst; /* jsr Abs.l */
- void (*ctx_LaunchEnter)(); /* ditto */
- }
-
- Thus, the actual switch/launch code of the task is part of the context
- structure. This, together with the magic cookie above allows identification
- of the context of the task with only the task pointer given. The library code
- on the other hand should remove the dummy return address of the jsr, subtract
- the offset of the ctx_Switch field from this address and gets, by this magic,
- a pointer to the Context data structure. The further code should extract
- the MMU user root pointer from this structure, compare it with the already
- active root pointer and install the new pointer and flush the ATC of the MMU
- if required.
-
- The tc_Switch entry has to call a routine that installs the common context
- root pointer, the tc_Launch entry has to install the private root pointer
- of the task. This has the advantage that we don't have to set the switch and
- launch entries of tasks not participating in the virtual memory buisiness,
- but the disadvantage that the MMU root pointer is set twice if a context
- switch to a task sharing the same context occurs. I've currently no idea
- how to fix this, but I guess the overhead of reloading the ATC isn't that
- big on the other hand.
-
- Exception handling
-
- The mmu library should install its own bus error exception vector in the
- exception vector offsets. On a bus error, the following should happen:
-
- - First, check if its a long word access to AbsExecBase. If so, run the
- specific emulator code of the processor. That's either trivial or tricky:
-
- For the '020 or '030, fill the input queue in the stack frame with the
- correct value, set some bits in the exception stack frame and continue
- execution.
-
- For the '040, check if some writes are still in the pipeline. If so, run
- the magic part, see below. If not, try first to decode the instruction
- manually. If it's an move.l AbsExecBase.w,a6 or a move.l AbsExecBase.l,a6,
- emulate it by software, adjust the exception stack frame and continue
- execution.
-
- For the '060, the exception model is much simpler. There's no write
- pipeline and we can basically use the same technique as above.
-
- And now for the tricky part:
-
- Emulation of the instruction access for the '040 and '060 if the
- instruction cannot decoded manually. This means that we've to turn off
- the protection of AbsExecBase, turn on tracing, execute the instruction
- in trace mode and turn on the protection afterwards. This means, too,
- that we might need to patch some Cache related calls of the exec.library.
- Urgh! Details of this method can be found in the enforcer code and the
- CyberGuard. At least, this kind of magic does already work.
-
- - If the access is NOT an access of AbsExecBase, then check the
- reason for the exception.
-
- If it is not caused by the MMU, select the bus error handler list
- for the next step.
-
- If it is caused by the MMU, get the context of the task that caused
- the exception by investigating its tc_Switch and tc_Launch fields.
- If no context can be found, then get the global common context.
-
- Check, in this context, the status of that address. If it is marked
- as invalid, or write protected and the access is a write access, or
- as supervisor only, and the task is in user state, then use the
- segmentation fault list for the next step. A future version of the
- enforcer might set this hook and may print out some useful
- information about the crash.
-
- If the status of the address is "swapped", use the "swapped fault"
- list of the context. This points, too, by default, to a routine that
- calls the exception handler of the task.
-
- - In the next step, check all handlers on the hook list, sorted by
- priority. Check for each hook if it applies to all tasks (the task
- field is NULL in this case), or if it applies to this task. In
- this case, build the ExceptionData structure on the stack, partially
- using information from the exception stack frame (esp. the return
- PC is useful here) and call the exception interrupt, by loading
- the registers as pointed out in the autodocs. If the handler returns
- 0, it was able to handle the exception and the MMU lib will simply
- continue with RTE, hoping that the handler modified the exception
- stack frame appropriately.
-
- - Check the exception data structure and the EXDF_CONTINUE flag.
- If that flag is not set, the CPU is allowed to rerun the faulty
- instruction (that's the easy case for the '060 and the most
- useful for the use of the memory.library).
-
- If the flag IS set, things become rather tricky.
-
- The mmu table must be modified temporarily to point to a "spare page"
- somewhere in memory. The value to be read must be set to the right
- position within this spare page, tracing must be enabled and
- the faulty instruction must be traced over. It will now read the
- word from the spare page we placed there, by using the modified
- MMU table.
- The trace exception will now occur - we then may restore the MMU
- table, disable tracing and continue the program.
- This is, indeed, very tricky. Especially the interaction with
- DMA is worth a lot of consideration, check the enforcer code
- for details how this magic can be done. Urgh.
-
- The message hooks:
-
- This is a tricky handler mechanism that builds on top of the
- hook mechanism pointed out above, but is somewhat more sophisticated
- and designed for the use of the memory library.
-
- A message hook will install a context hook of the kind described
- above. The hook function will then:
-
- -check wether task switching is enabled or not. If it's not, it
- cannot perform its operation and will return with -1 - the
- next handler of the exception chain will be called then.
- -"Bend" the return PC to its own function and keep the original
- return PC in the message hook structure,
- - copy the exception data to the exception message embedded
-
- resume operation afterwards.
-
- The faulty task will then continue execution of the message
- exception handler of the mmu.library. This function will now
- send the build message to the port indicated in the exception hook
- data and will, furthermore, wait for return of the message on the
- reply port which is embedded in the hook data as well. Since I
- don't want to spend a user signal for this, I'll have to use
- an undocumented system signal for this port (yuk!)
-
- If the message returns, get the return PC from the message data
- (which might be modified again by the task that received that
- message), reload all registers and continue execution of the user
- program.
-
- The memory library:
-
- CreateAdrSpace has basically to call CreateContext() of the mmu.library,
- and has to install its own swapped hook into that context. This will be
- simply a message hook of the type above with the port set to the global
- port of the swapper daemon.
-
- o) The swapper daemon uses the logical address and the context to determinate
- the location of the swapped out page on disk, either in a file or in a
- disk partition. The details HOW the swapped blocks are kept on disk have still
- to be worked out, as well as the algorithm of which pages are swapped.
-
- This means in detail:
- - Try to allocate a physical memory block for the page. If that fails,
- swap out another page and mark it invalid. Then use this page.
-
- - Read the data from disk.
-
- - Reply the message which was build by the library code.
-
- o) The user code can now safely clean up the stack and retry the instruction
- that caused the exception.
-
-